/*
 * Copyright (C) 2014 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.common.math;

import com.google.caliper.BeforeExperiment;
import com.google.caliper.Benchmark;
import com.google.caliper.Param;
import com.google.common.collect.ContiguousSet;
import com.google.common.collect.DiscreteDomain;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Range;
import java.util.Random;

/**
 * Benchmarks some algorithms providing the same functionality as {@link Quantiles}.
 */
public class QuantilesBenchmark {

    private static final ContiguousSet<Integer> ALL_DECILE_INDEXES =
            ContiguousSet.create(Range.closed(0, 10), DiscreteDomain.integers());

    @Param({"10", "100", "1000", "10000", "100000"})
    int datasetSize;

    @Param
    QuantilesAlgorithm algorithm;

    private double[][] datasets = new double[0x100][];

    @BeforeExperiment
    void setUp() {
        Random rng = new Random();
        for (int i = 0; i < 0x100; i++) {
            datasets[i] = new double[datasetSize];
            for (int j = 0; j < datasetSize; j++) {
                datasets[i][j] = rng.nextDouble();
            }
        }
    }

    private double[] dataset(int i) {
        // We must test on a fresh clone of the dataset each time. Doing sorts and quickselects on
        // an
        // dataset which is already sorted or partially sorted is cheating.
        return datasets[i & 0xFF].clone();
    }

    @Benchmark
    double median(int reps) {
        double dummy = 0.0;
        for (int i = 0; i < reps; i++) {
            dummy += algorithm.singleQuantile(1, 2, dataset(i));
        }
        return dummy;
    }

    @Benchmark
    double percentile90(int reps) {
        double dummy = 0.0;
        for (int i = 0; i < reps; i++) {
            dummy += algorithm.singleQuantile(90, 100, dataset(i));
        }
        return dummy;
    }

    @Benchmark
    double percentile99(int reps) {
        double dummy = 0.0;
        for (int i = 0; i < reps; i++) {
            dummy += algorithm.singleQuantile(99, 100, dataset(i));
        }
        return dummy;
    }

    @Benchmark
    double percentiles90And99(int reps) {
        double dummy = 0.0;
        for (int i = 0; i < reps; i++) {
            dummy += algorithm.multipleQuantiles(ImmutableSet.of(90, 99), 100, dataset(i)).get(90);
        }
        return dummy;
    }

    @Benchmark
    double threePercentiles(int reps) {
        double dummy = 0.0;
        for (int i = 0; i < reps; i++) {
            dummy += algorithm.multipleQuantiles(ImmutableSet.of(90, 95, 99), 100, dataset(i)).get(90);
        }
        return dummy;
    }

    @Benchmark
    double allDeciles(int reps) {
        double dummy = 0.0;
        for (int i = 0; i < reps; i++) {
            dummy += algorithm.multipleQuantiles(ALL_DECILE_INDEXES, 10, dataset(i)).get(9);
        }
        return dummy;
    }
}
